#include "matrix.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
//~ #include "mpi_tools.h"
#include "opencl_tools.h"

// 2D Matrix 
tMatrix **Matrix2Allocate(Point2 offset, Point2 size){
    tMatrix **matrix;
    tMatrix *data;
    int nb_elements = size.x * size.y;
    
    messagecl("Allocate matrix (%d,%d) %d o (plus pointeurs : %d o)", size.x, size.y,  
            nb_elements * sizeof(tMatrix),
            size.x * sizeof(tMatrix*));

    data = (tMatrix *)secure_malloc_cl(nb_elements * sizeof(tMatrix));
    matrix = (tMatrix **)secure_malloc_cl(size.x * sizeof(tMatrix *));

    matrix -= offset.x;
    for(int x = offset.x; x < offset.x + size.x; x++)
        matrix[x] = data + size.y * (x - offset.x) - offset.y;

    return matrix;
}

void Matrix2Free(tMatrix **matrix, Point2 offset){
    free(&matrix[offset.x][offset.y]);
    free(&matrix[offset.x]);
}

void Matrix2Print(tMatrix **matrix, Point2 offset, Point2 size){
    printf("Matrix2Print, size(%d, %d) offset(%d, %d)\n", size.x, size.y, offset.x, offset.y);
    for(int y = offset.y; y < offset.y + size.y; y++){
        for(int x = offset.x; x < offset.x + size.x; x++)
            printf("%.1e, ", matrix[x][y]);
        printf("EOL\n");
    }
}

// 3D Matrix
void ***Matrix3Allocate(Point3 offset, Point3 size, int dataSize){
    void *matrix_data;
    void **matrix_data_ptr;
    void ***matrix;
    int nb_elements = Matrix3GetNbElements(size);

    messagecl("Allocate matrix (%d,%d,%d) %d ko (plus pointeurs : %d ko)", 
            size.x, size.y, size.z, nb_elements * dataSize / 1024,
            size.x * ( 1 + size.y ) * sizeof(void*) / 1024);

    matrix = (void ***)secure_malloc_cl(size.x * sizeof(void**));
    matrix_data_ptr = (void **)secure_malloc_cl(size.x * size.y * sizeof(void*));
    matrix_data = (void *)secure_malloc_cl(nb_elements * dataSize);

    matrix -= offset.x;
    for(int x = offset.x; x < offset.x + size.x; x++)
        matrix[x] = matrix_data_ptr + size.y * (x - offset.x) - offset.y;

    for(int x = offset.x; x < offset.x + size.x; x++)
        for(int y = offset.y; y < offset.y + size.y; y++)
            matrix[x][y] = matrix_data +
                    ((x - offset.x) * size.y * size.z +
                (y - offset.y) * size.z - offset.z) * dataSize;

    return matrix;
}

int Matrix3GetNbElements(Point3 size){
    return size.x * size.y * size.z;
}

void Matrix3Free(void ***matrix, Point3 offset, int dataSize){  // Bug si offset.z != 0
    free(matrix[offset.x][offset.y] + dataSize * offset.z);
    free(&matrix[offset.x][offset.y]);
    free(&matrix[offset.x]);
}

void Matrix3Init(tMatrix ***matrix, Point3 offset, Point3 size, tMatrix value){
    for(int x = offset.x; x < offset.x + size.x; x++)
        for(int y = offset.y; y < offset.y + size.y; y++)
            for(int z = offset.z; z < offset.z + size.z; z++)
                matrix[x][y][z] = value;
}

void Matrix3Swap(tMatrix ****matrix1, tMatrix ****matrix2){
    tMatrix ***temps;
    
    temps = *matrix1;
    *matrix1 = *matrix2;
    *matrix2 = temps;
}

void Matrix3Print(tMatrix ***matrix, Point3 offset, Point3 size){

    for(int z = 1; z < 3; z++ ){
        printf("Plane z=%d\n", z);
        for(int y = offset.y; y < offset.y + size.y && y < 35; y++){
            for(int x = offset.x; x < offset.x + size.x && x < 33; x++)
                printf("%3.1f, ", matrix[x][y][z]);
            printf("\n");
        }
    }
}

void Matrix3Copy(tMatrix ***src, tMatrix ***dst, Point3 offset, Point3 size){
    memcpy(&dst[offset.x][offset.y][offset.z], &src[offset.x][offset.y][offset.z], 
            Matrix3GetNbElements(size) * sizeof(tMatrix));
}
